#include "Sphere.h"

#include <GL/glu.h>
#include <cmath>

#include <iostream>
using std::cout;
using std::endl;

int Sphere::SLICES = 16; // ccw
int Sphere::STACKS = 16; // upwards

Sphere::Sphere():
		m_r(1.0f)
{
	init();
}

Sphere::Sphere(float r):
		m_r(r)
{
	init();
}

Sphere::Sphere(float r, const Vertex& pos):
		Assembly(pos), m_r(r)
{
	init();
}

Sphere::Sphere(float r, float x, float y, float z):
		Assembly(x, y, z), m_r(r)
{
	init();
}

Sphere::~Sphere()
{
	for (vector<vector<Vertex*> >::iterator tit = m_vertex.begin(); tit != m_vertex.end(); ++tit)
	{
		for (vector<Vertex*>::iterator sit = tit->begin(); sit != tit->end(); ++sit)
		{
			delete *sit;
		}
	}
	for (vector<vector<Vertex*> >::iterator tit = m_normal.begin(); tit != m_normal.end(); ++tit)
	{
		for (vector<Vertex*>::iterator sit = tit->begin(); sit != tit->end(); ++sit)
		{
			delete *sit;
		}
	}
	
}

const float& Sphere::radius() const
{
	return m_r;
}

float& Sphere::radius()
{
	m_v_mod = true;
	return m_r;
}

void Sphere::init()
{
	m_name = "sphere";
	
	// 2 more stacks for the north- and southpole: 0 and STACKS
	m_vertex.resize(STACKS+1);
	m_vertex[0].resize(1);
	m_vertex[0][0] = new Vertex(0.0f, m_r, 0.0f);
	
	m_normal.resize(STACKS+1);
	m_normal[0].resize(1);
	m_normal[0][0] = new Vertex(0.0f, m_r, 0.0f, 0.0f);
	
	for (int t = 1; t < STACKS; ++t)
	{
		m_vertex[t].resize(SLICES);
		m_normal[t].resize(SLICES);
		for (int s = 0; s < SLICES; ++s)
		{
			m_vertex[t][s] = new Vertex;
			m_normal[t][s] = new Vertex(0.0f, 0.0f, 0.0f, 0.0f);
		}
	}
	
	m_vertex[STACKS].resize(1);
	m_vertex[STACKS][0] = new Vertex(0.0f, -m_r, 0.0f);
	
	m_normal[STACKS].resize(1);
	m_normal[STACKS][0] = new Vertex(0.0f, -m_r, 0.0f, 0.0f);
	
	
	// poles & Co
	Face *top = new Face(false), *bottom= new Face(false);
	for (int s = 0; s < SLICES; ++s)
	{
		int si = (s+1) % SLICES;
		top->push_back(m_vertex[0][0], m_normal[0][0]);
		top->push_back(m_vertex[1][s], m_normal[1][s]);
		top->push_back(m_vertex[1][si], m_normal[1][si]);
		
		bottom->push_back(m_vertex[STACKS][0], m_normal[STACKS][0]);
		bottom->push_back(m_vertex[STACKS-1][si], m_normal[STACKS-1][si]);
		bottom->push_back(m_vertex[STACKS-1][s], m_normal[STACKS-1][s]);
	}
	m_face.push_back(top);
	m_face.push_back(bottom);
	
	// body
	for (int t = 1; t < STACKS-1; ++t)
	{
		int ti = (t+1) % STACKS;
		for (int s = 0; s < SLICES; ++s)
		{
			int si = (s+1) % SLICES;
			Face* face = new Face(false);
			face->push_back(m_vertex[t][s], m_normal[t][s]);
			face->push_back(m_vertex[ti][s], m_normal[ti][s]);
			face->push_back(m_vertex[ti][si], m_normal[ti][si]);
			face->push_back(m_vertex[t][si], m_normal[t][si]);
			m_face.push_back(face);
		}
	}
}

void Sphere::update_vertex()
{
	*m_vertex[0][0] = Vertex(0.0f, m_r, 0.0f);
	*m_vertex[STACKS][0] = Vertex(0.0f, -m_r, 0.0f);
	
	*m_normal[0][0] = Vertex(0.0f, m_r, 0.0f);
	*m_normal[STACKS][0] = Vertex(0.0f, -m_r, 0.0f);
	
	for (int t = 1; t < STACKS; t++)
	{
		float phi = M_PI * static_cast<float>(t) / STACKS;
		float y = m_r * cos(phi), r_sp = m_r * sin(phi);
		
		for (int s = 0; s < SLICES ; s++)
		{
			float theta = 2 * M_PI * static_cast<float>(s) / SLICES;
			m_vertex[t][s]->setAll(r_sp*sin(theta), y, r_sp*cos(theta));
			*m_normal[t][s] = *m_vertex[t][s];
		}
	}
	Assembly::update_vertex();
}

